import java.io.FileNotFoundException;

/**
 * Contains all necessary information and accessor methods to serve as the model for a 
 * game of Jotto.
 * 
 * @author Sam
 *
 */
public class JottoModel implements GameModel {
	
	/**
	 * Dictionary used for word lookup and comparison.
	 */
	private AbstractDictionary dict;
	
	/**
	 * If the last guess was a perfect match is true, false otherwise.
	 */
	private boolean lastmatched;
	
	/**
	 * Maximum number of allowed guesses.
	 */
	private int guesslimit;
	
	/**
	 * Player object which represents Player1's data.
	 */
	private Player p1;
	
	/**
	 * Player object which represents Player1's data.
	 */
	private Player p2;
	
	/**
	 * Count the number of guesses made by player1.
	 */
	private int p1guesses;
	
	/**
	 * Count the number of guesses made by player2.
	 */
	private int p2guesses;
	
	/**
	 * 
	 * @param guessLimit the total number of guesses allowed in a game
	 * @param dict the dictionary to use for word validation
	 * @throws FileNotFoundException if the dictionary file cannot be loaded
	 */
	public JottoModel(int guessLimit, String dict) throws FileNotFoundException {
		p1guesses = 0;
		p2guesses = 0;
		this.guesslimit = guessLimit;
		this.dict = new Dictionary(dict);
		p1 = new ActivePlayer(this.dict);
		p2 = new ActivePlayer(this.dict);
	}
	
	
	/**
	 * Checks that a word is in the dictionary.
	 * 
	 * @param word the word to be checked
	 * @return word is in dictionary
	 */
	@Override
	public boolean wordValid(String word) {
		return dict.containsWord(word);
	}


	/**
	 * Sets the specified player's secret word.
	 * 
	 * @param p the player in question
	 * @param secret the word being defined as that player's secret word
	 * @throws WordException if the word is not in the dictionary
	 */
	@Override
	public void setSecretWord(PlayerSelect p, String secret) throws WordException {
		switch (p) {
		case P1:
			p1.setWord(secret);
			break;
		default:
			p2.setWord(secret);
		}

	}


	/**
	 * {@inheritDoc}
	 * Increments the guesscount of the player who is not the target of the guess.
	 * 
	 * @throws WordException if the word is not in the dictionary
	 */
	@Override
	public int guess(PlayerSelect p, String word) throws WordException {
		int[] sharedLetters = {0};
		lastmatched = false;
		
		switch (p) {
		case P1:
			p2guesses++;
			lastmatched = p1.guess(word, sharedLetters);
			// If a player has hit the guess limit without guessing correctly don't award
			// any points
			if (p2guesses >= guesslimit && !lastmatched) {
				p2guesses++;
			}
			break;
		default:
			p1guesses++;
			lastmatched = p2.guess(word, sharedLetters);
			if (p1guesses >= guesslimit && !lastmatched) {
				p1guesses++;
			}
		}
		return sharedLetters[0];
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean lastPerfectlyMatched() {
		return lastmatched;
	}



	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean guessLimitReached(PlayerSelect p) {
		switch(p) {
		case P1:
			return p1guesses >= guesslimit;
		default:
			return p2guesses >= guesslimit;
		}
		
	}


	/**
	 * Award players wins and/or points based on who took fewer guesses to get their 
	 * opponent's word. The maximum possible number of points awarded in a single game is
	 * 100. Each unsuccessful guess reduces the number of points awarded according to the
	 * formula 100 - numguesses * 100/guesslimit where the guess limit is the maximum
	 * possible number of guesses before a player is awarded 0 points.
	 * 
	 * @return {@inheritDoc}
	 */
	@Override
	public PlayerSelect concludeGame() {
		final int maxscore = 100;
		int p1awarded = 
			Math.round(maxscore - (p1guesses - 1) * ((float) maxscore) / guesslimit);
		int p2awarded =
			Math.round(maxscore - (p2guesses - 1) * ((float) maxscore) / guesslimit);
		
		// In case things round weird...
		if (p1awarded < 0) {
			p1awarded = 0;
		}
		if (p2awarded < 0) {
			p2awarded = 0;
		}
		
		if (p1guesses < p2guesses) {
			p1.win(p1awarded);
			p2.lose(p2awarded);
			return PlayerSelect.P1;
		} else if (p2guesses < p1guesses) {
			p1.lose(p1awarded);
			p2.win(p2awarded);
			return PlayerSelect.P2;
		} else {
			p1.win(p1awarded);
			p2.win(p2awarded);
			return null;
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getScore(PlayerSelect p) {
		switch(p) {
		case P1:
			return p1.getScore();
		default:
			return p2.getScore();
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getWins(PlayerSelect p) {
		switch(p) {
		case P1:
			return p1.getWins();
		default:
			return p2.getWins();
		}
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public void newGame() {
		p1guesses = 0;
		p2guesses = 0;
		lastmatched = false;
	}
	
}
